﻿#include "chatmanage.h"

#include <utils/consoleprocess.h>
#include <utils/environment.h>
#include <utils/hostosinfo.h>
#include <utils/qtcprocess.h>
#include <utils/textfileformat.h>
#include <utils/unixutils.h>

#include <QApplication>
#include <QNetworkInterface>
#include <QDebug>
#include <QString>
#include <QTime>
#include <QUrl>
#include <QJsonObject>
#include <QClipboard>

ChatManage::ChatManage(QObject *param,AllSqlConversationModel *psql,SqlContactModel *pcont):
    QObject(param),m_Network(false),m_sqlModel(psql),m_sqlContact(pcont),m_exit(false)
{
    m_random = 0;
    m_time = 60000*5;
    m_user = "root";
    m_Registe = false;

    //ec2:My4xNDIuMTU5LjUw
    //虚拟机:MTkyLjE2OC4xNTcuMTI5
    serPort = getConfigValue("Server/PORT").toInt();
    serIP = getConfigValue("Server/IP");
    serPortF = getConfigValue("Server/PORTF").toInt();
    psql->serPortF = QString("%1").arg(serPortF);
    const QDir writeDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    if (!writeDir.mkpath("."))
        qFatal("Failed to create writable directory at %s", qPrintable(writeDir.absolutePath()));

    // Ensure that we have a writable location on all devices.
    //    const QString fileName = writeDir.absolutePath() + "/chat-database.sqlite3";
    if(getConfigValue("SoftWare/TempPath").isEmpty()){
        writeConfigValue("SoftWare/TempPath","file:///"+writeDir.absolutePath());
        //        m_MyConfig.insert("SoftWare/TempPath","file:///"+writeDir.absolutePath());
    }
    else{
        m_MyConfig.insert("SoftWare/TempPath",getConfigValue("SoftWare/TempPath"));
    }

    if(getConfigValue("SoftWare/SavePath").isEmpty()){
        writeConfigValue("SoftWare/SavePath","file:///"+writeDir.absolutePath());
        //        m_MyConfig.insert("SoftWare/TempPath","file:///"+writeDir.absolutePath());
    }
    else{
        m_MyConfig.insert("SoftWare/SavePath",getConfigValue("SoftWare/SavePath"));
    }


    qDebug()<<m_MyConfig;

    QJsonObject value;
    value.insert("ip",serIP);
    value.insert("port",serPort);

    p_Contrel = new Controller();
    connect(p_Contrel,SIGNAL(sigMessageRev(int,int,QString)),this,SLOT(MessageRev(int,int,QString)));
    connect(p_Contrel,SIGNAL(sigSocketStateChange(int,QString)),this,SLOT(SocketStateChange(int,QString)));
    p_Contrel->setUpdateControlStart(value);

    connect(m_sqlModel,SIGNAL(sendToServer(int,QString)),this,SLOT(sendToServer(int,QString)));
    connect(this,SIGNAL(sendToSql(QJsonObject)),m_sqlModel,SLOT(sendToSql(QJsonObject)));

    connect(this,SIGNAL(sendToSqlFile(QJsonObject)),m_sqlModel,SLOT(sendToSqlForFile(QJsonObject)));
    connect(m_sqlModel,SIGNAL(downloadFile(QJsonObject)),this,SLOT(downloadFile(QJsonObject)));

    connect(m_sqlModel,SIGNAL(uploadFile(QJsonObject)),this,SLOT(uploadFile(QJsonObject)));

    m_Curl.reset(new Curl::Controller);
    connect(m_Curl.data(),SIGNAL(getReturn(QJsonObject)),m_sqlModel,SLOT(uploadFileReturn(QJsonObject)));

    m_DownLoad.reset(new HttpLoading::Controller);
    connect(m_DownLoad.data(),SIGNAL(getReturn(QJsonObject)),m_sqlModel,SLOT(downLoadFileReturn(QJsonObject)));
    pRegisteDelay = new QTimer;
    pRegisteDelay->setSingleShot(true);
    //    process = new QProcess(this);
    //    connect(process , SIGNAL(readyReadStandardOutput()) , this , SLOT(on_readoutput()));
    //    connect(process , SIGNAL(readyReadStandardError()) , this , SLOT(on_readerror()));
    //    connect(process , SIGNAL(finished(int,QProcess::ExitStatus)) , this , SLOT(on_finished(int,QProcess::ExitStatus)));
    //    connect(process , SIGNAL(errorOccurred(QProcess::ProcessError)) , this , SLOT(on_errorOccurred(QProcess::ProcessError)));
    //    connect(process , SIGNAL(stateChanged(QProcess::ProcessState, QPrivateSignal)) , this , SLOT(on_stateChanged(QProcess::ProcessState state, QPrivateSignal)));
}
void ChatManage::test(){
    //    qDebug()<<qApp->allWidgets();
    //    qApp->setWindowIcon(QIcon(":/shared/test.ico"));
    //QApplication::alert(p->,5000);
}

ChatManage::~ChatManage()
{
    if(p_Contrel)
        delete p_Contrel;
}

void ChatManage::downloadFile(QJsonObject value)
{
    value.insert("filePath",MyConfig()["SoftWare/TempPath"]);
    m_DownLoad->addNewTask(value);
}

void ChatManage::uploadFile(QJsonObject value)
{
    value.insert("serverPath","http://"+m_piIp+QString(":%1/").arg(serPortF));

    m_Curl->addNewTask(value);
}

void ChatManage::openTempFile(const QString &path)
{
    qDebug()<<path;
    QString tempPath;
    if(path.startsWith("file:")){
        tempPath = path.right(path.length()-8);
    }

    using namespace Utils;
    const QFileInfo fileInfo(tempPath);
    // Mac, Windows support folder or file.
    if (HostOsInfo::isWindowsHost()) {
        const FilePath explorer = Environment::systemEnvironment().searchInPath(QLatin1String("explorer.exe"));
        if (explorer.isEmpty()) {
            qDebug()<<"run explorer failed";
            return;
        }
        QStringList param;
        if (!fileInfo.isDir())
            param += QLatin1String("/select,");
        param += QDir::toNativeSeparators(fileInfo.canonicalFilePath());
        QProcess::startDetached(explorer.toString(), param);
    } else if (HostOsInfo::isMacHost()) {
        QStringList scriptArgs;
        scriptArgs << QLatin1String("-e")
                   << QString::fromLatin1("tell application \"Finder\" to reveal POSIX file \"%1\"")
                          .arg(fileInfo.canonicalFilePath());
        QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
        scriptArgs.clear();
        scriptArgs << QLatin1String("-e")
                   << QLatin1String("tell application \"Finder\" to activate");
        QProcess::execute(QLatin1String("/usr/bin/osascript"), scriptArgs);
    } else {
        // we cannot select a file here, because no file browser really supports it...
        qWarning()<<"unsupport os";
    }

}

QString ChatManage::GetCorrectUnicode(const QByteArray &ba)
{
    QTextCodec::ConverterState state;
    QTextCodec *codec = QTextCodec::codecForName("UTF-8");
    QString text = codec->toUnicode(ba.constData(), ba.size(), &state);
    if (state.invalidChars > 0)
    {
        text = QTextCodec::codecForName("GBK")->toUnicode(ba);
    }
    else
    {
        text = QString::fromStdString(ba.toStdString());
    }
    return text;
}

void ChatManage::toGetError(int type,int subIndex, QString message)
{
    emit getError(type,subIndex,message);
}

QJsonObject ChatManage::getOneUserData(const QString &listvalue)
{
    QJsonObject rvalue;
    QStringList element = listvalue.split("|");
    foreach (QString var, element) {
        if(var.contains(":")){
            QStringList one = var.split(":");
            if(one.value(0)=="id")
                rvalue.insert(one.value(0),one.value(1));
            else
                rvalue.insert(one.value(0),QString::fromUtf8(QByteArray::fromBase64(one.value(1).toUtf8())));
        }
    }
    return rvalue;
}

QString ChatManage::formateQJsonObject(const QJsonObject &jsonvalue)
{
    QString list;
    QJsonObject::const_iterator it = jsonvalue.constBegin();
    QJsonObject::const_iterator end = jsonvalue.constEnd();
    while (it != end) {
        if(it.key()=="id")
            list+=it.key()+":"+it.value().toString()+"|";
        else
            list+=it.key()+":"+QString::fromUtf8(it.value().toString().toUtf8().toBase64())+"|";
        it++;
    }
    return list.left(list.length()-1);
}
void ChatManage::initRegiste(const QJsonObject &jsonvalue)
{
    m_sqlContact->updateCurrentUserMail(jsonvalue["user_mail"].toString());
    m_sqlContact->sendToSql(jsonvalue);
    m_sqlModel->initCurrentUser(jsonvalue);
}

void ChatManage::MessageRev(int type,int subtype,QString str){
    qDebug()<<type<<subtype<<str;
    switch (subtype) {
    case serverthread::Register:
        if(type){
            Registe(false);
            emit loginAnswer(m_Registe,str);
        }else{
            MyInfo(getOneUserData(str));
            initRegiste(m_MyInfo);
            Registe(true);
            sendGetPiIp();
            emit loginAnswer(m_Registe,"");
        }

        break;
    case serverthread::NewUser:
        if(Registe()){
            QJsonObject rv = getOneUserData(str);
            //            if(!m_onlineUser.contains(rv.value("user_mail").toString())){
            //                m_onlineUser.insert(rv.value("user_mail").toString(),rv);
            //            m_view->insertValue(rv.value("user_mail").toString(),rv);
            m_sqlContact->sendToSql(rv);
            //            }
            package(1001,rv.value("user_mail").toString().toUtf8().toBase64()+"|"+formateQJsonObject(m_MyInfo));
        }
        break;
    case serverthread::AddNewUser:
        if(str.startsWith("success",Qt::CaseInsensitive)){
            return;
        }
        if(Registe()){
            QJsonObject rv = getOneUserData(str);
            //            if(!m_onlineUser.contains(rv.value("user_mail").toString())){
            //            m_view->insertValue(rv.value("user_mail").toString(),rv);
            m_sqlContact->sendToSql(rv);
            //                m_onlineUser.insert(rv.value("user_mail").toString(),rv);
            //            }
        }
        break;
    case serverthread::RemoveUser:
        if(Registe()){
            //m_onlineUser.remove(QString::fromUtf8(QByteArray::fromBase64(str.toUtf8())));
            //m_view->removeValue(QString::fromUtf8(QByteArray::fromBase64(str.toUtf8())));
            m_sqlContact->newUserOffline(QString::fromUtf8(QByteArray::fromBase64(str.toUtf8())));
        }
        break;

    case serverthread::NewMessageReturn:

        break;
    case serverthread::NewMessage:
        if(str.startsWith("success",Qt::CaseInsensitive)){
            return;
        }
        if(Registe()){
            QStringList value = str.split("|");
            QJsonObject sv;
            sv["author"]=QString::fromUtf8(QByteArray::fromBase64(value.value(0).toUtf8()));
            sv["recipient"]=QString::fromUtf8(QByteArray::fromBase64(value.value(1).toUtf8()));
            sv["message"]=QString::fromUtf8(QByteArray::fromBase64(value.value(2).toUtf8()));
            sv["timestamp"]=value.value(3);
//            qDebug()<<sv;
            emit sendToSql(sv);
            if(sv["author"].toString()=="GROUP")
                m_sqlContact->getNewMessage(sv["author"].toString());
            else
                m_sqlContact->getNewMessage(sv["recipient"].toString());
        }
        break;
    case serverthread::ConfigSetReturn:
        if(!Registe()){
            return;
        }
        if(str.startsWith("success",Qt::CaseInsensitive)){
            MyInfo(m_MyInfo_Temp);
            writeConfigValue("UserInfo/Pwd",m_MyInfo_Temp["user_pwd"].toString());
        }
        else{
            emit getError(1,0,QString("%1").arg(str));
        }

        break;
    case serverthread::ConfigSet:
        if(Registe()){
            QJsonObject rv = getOneUserData(str);
            m_sqlContact->sendToSql(rv);
            m_sqlModel->submitAll();
        }

        break;
    case serverthread::NewMessageFileReturn:
        if(str.startsWith("success",Qt::CaseInsensitive)){

        }
        else{
            emit getError(1,0,QString("%1").arg(str));
        }
        break;
    case serverthread::NewMessageFile:
        if(str.startsWith("success",Qt::CaseInsensitive)){
            return;
        }
        if(Registe()){
            QStringList value = str.split("|");
            QJsonObject sv;
            sv["author"]=QString::fromUtf8(QByteArray::fromBase64(value.value(0).toUtf8()));
            sv["recipient"]=QString::fromUtf8(QByteArray::fromBase64(value.value(1).toUtf8()));
            sv["message"]=QString::fromUtf8(QByteArray::fromBase64(value.value(2).toUtf8()));
            sv["timestamp"]=value.value(3);
            emit sendToSqlFile(sv);
            //emit sendToSqlForFile(sv);
            if(sv["author"].toString()=="GROUP")
                m_sqlContact->getNewMessage(sv["author"].toString());
            else
                m_sqlContact->getNewMessage(sv["recipient"].toString());
        }

        break;
    case serverthread::NewMessageFileGroup:
        if(str.startsWith("success",Qt::CaseInsensitive)){
            return;
        }
        if(Registe()){
            QStringList value = str.split("|");
            QJsonObject sv;
            sv["author"]=QString::fromUtf8(QByteArray::fromBase64(value.value(0).toUtf8()));
            sv["recipient"]=QString::fromUtf8(QByteArray::fromBase64(value.value(1).toUtf8()));
            sv["message"]=QString::fromUtf8(QByteArray::fromBase64(value.value(2).toUtf8()));
            sv["timestamp"]=value.value(3);
            emit sendToSqlFile(sv);
            //emit sendToSqlForFile(sv);
            if(sv["author"].toString()=="GROUP")
                m_sqlContact->getNewMessage(sv["author"].toString());
            else
                m_sqlContact->getNewMessage(sv["recipient"].toString());
        }
        break;
    case serverthread::GetPiIP:
        m_piIp = str;
        emit myPiChanged();
        break;
    default:
        break;
    }

    //    emit getError(type,0,str);
}

void ChatManage::SocketStateChange(int state,QString str){
    switch (state) {
    case QAbstractSocket::SocketState::UnconnectedState:
        Network(false);
        break;
    case QAbstractSocket::SocketState::HostLookupState:

        break;
    case QAbstractSocket::SocketState::ConnectingState:

        break;
    case QAbstractSocket::SocketState::ConnectedState:
        Network(true);
        break;
    case QAbstractSocket::SocketState::BoundState:

        break;
    case QAbstractSocket::SocketState::ListeningState:

        break;
    case QAbstractSocket::SocketState::ClosingState:
        Network(false);
        break;
    default:
        break;
    }
    emit getError(state,1,str);
}

void ChatManage::sendToServer(int type,QString value)
{
    package(type,value);
}

void ChatManage::package(int type,QString value){
    QByteArray stv;
    stv.append(value.toUtf8());
    QString pagedata = QString("@@%1%2").arg(stv.size()+10,4,10,QLatin1Char('0')).arg(type,4,10,QLatin1Char('0'));
    stv.insert(0,pagedata.toUtf8());

    stv.append('\n');

    emit p_Contrel->sendData(stv);
    //    emit senddata(stv);
}

void ChatManage::sendRegiste(const QString &user_mail,const QString &user_pwd){
    if(pRegisteDelay->isActive()){
        qWarning()<<"ignore registe";
        return;
    }
    pRegisteDelay->start(2000);
    m_MyInfo.insert("user_mail",user_mail);
    m_MyInfo.insert("user_pwd",user_pwd);
    m_exit = false;
    package(1,QString::fromUtf8(user_mail.toUtf8().toBase64()+"|"+ user_pwd.toUtf8().toBase64()));//"a29uZ193QGZveG1haWwuY29t|MTIz"
}

void ChatManage::sendGetPiIp()
{
    package(4001,"");
}

QString ChatManage::MyPi()
{
    return m_piIp;
}

void ChatManage::setUserConfig(QString name,QString pwd,QString img)
{
    int changeFlag=0;
    m_MyInfo_Temp = m_MyInfo;
    if(name!=m_MyInfo["user_name"].toString()){
        changeFlag++;
        m_MyInfo_Temp["user_name"] = name;
    }
    if(pwd!=m_MyInfo["user_pwd"].toString()&&!pwd.isEmpty()){
        changeFlag++;
        m_MyInfo_Temp["user_pwd"] = pwd;
    }
    if(img!=m_MyInfo["user_img"].toString()){
        changeFlag++;
        m_MyInfo_Temp["user_img"] = img;
    }
    if(changeFlag>0){
        package(2100,"user_mail:"+QString::fromUtf8(m_MyInfo_Temp["user_mail"].toString().toUtf8().toBase64())
                          +"|user_name:"+QString::fromUtf8(m_MyInfo_Temp["user_name"].toString().toUtf8().toBase64())
                          +"|user_pwd:"+QString::fromUtf8(m_MyInfo_Temp["user_pwd"].toString().toUtf8().toBase64())
                          +"|user_img:"+QString::fromUtf8(m_MyInfo_Temp["user_img"].toString().toUtf8().toBase64()));
    }
    else
        qDebug()<<"nothing config changed!";
}

//void ChatManage::addtoUpdateList(QJsonObject json){
//    //qDebug()<<QString("%1%2|update|").arg(type,4,10,QLatin1Char('0')).arg(ip);
//    package(3,QString("%1%2|update&%3|").arg(json.value("type").toInt(),4,10,QLatin1Char('0')).arg(json.value("ip").toString()).arg(json.value("url").toString()));
//}

void ChatManage::sendTCP(QString type,QString version,QString confirm,QString state,QString other){
    QStringList value;
    //value.append(type);

    value.append(version);
    value.append(confirm);
    value.append(state);
    value.append(other);

    QDateTime current_date_time =QDateTime::currentDateTime();
    //value.append(current_date_time.toString("yyyyMMddhhmmss"));
    QByteArray stv;
    stv.append(value.join("").toUtf8());
    //qDebug()<<stv.size()<<stv;
    QString pagedata = QString("@@%1%2").arg(stv.size()+10,4,10,QLatin1Char('0')).arg(type.toInt(),4,10,QLatin1Char('0'));
    //qDebug()<<pagedata;

    stv.insert(0,pagedata.toUtf8());
    //qDebug()<<stv;
    stv.append('\n');
    emit p_Contrel->sendData(stv);
    //    emit senddata(stv);
}

QStringList ChatManage::getHostIpMac(){
    QStringList rvalue;
    QList<QNetworkInterface> nets = QNetworkInterface::allInterfaces();
    foreach (QNetworkInterface var, nets){
        if(var.flags().testFlag(QNetworkInterface::IsUp)&&var.flags().testFlag(QNetworkInterface::IsRunning)&&!var.flags().testFlag(QNetworkInterface::IsLoopBack)){
            rvalue.append(var.hardwareAddress());
            QList<QHostAddress> ipAddressesList = var.allAddresses();
            foreach (QHostAddress var, ipAddressesList) {
                if(var != QHostAddress::LocalHost && var.toIPv4Address()){
                    rvalue.append(var.toString());
                }
            }
        }
    }

    for(int i=rvalue.size()-1;i>0;i--){
        if(QString(rvalue.value(i)).left(5).contains("192")){
            rvalue.removeAt(i);
        }
    }
    //qDebug()<<rvalue;
    if(rvalue.size()==0){
        rvalue.append("00:00:00:00:00:00");
        rvalue.append("0.0.0.0");
    }
    else if(rvalue.size()==1){
        rvalue.append("0.0.0.0");
    }
    return rvalue;
}

void ChatManage::getData(int hd,int code,QString answer){
    if(m_random!=hd){
        qWarning()<<"not the same socket";
        return;
    }
    if(code){//success
        qDebug()<<hd<<code<<answer;
    }
    else{
        emit getError(1,0,QString("[%1]%2").arg(code).arg(answer));
    }
}

void ChatManage::error(int hd,int socketError, const QString &message){
    if(m_random!=hd){
        qWarning()<<"not the same socket";
        return;
    }
    emit getError(1,0,QString("[%1]%2").arg(socketError).arg(message));
}
QString ChatManage::getConfigValue(QString key)
{
    QString filePath = QCoreApplication::applicationDirPath() + "/chat.ini";
    QSettings setting(filePath,QSettings::IniFormat);
    return QString::fromUtf8(QByteArray::fromBase64(setting.value(key).toString().toUtf8()));
}
void ChatManage::writeConfigValue(QString key,QString Value)
{
    QString filePath = QCoreApplication::applicationDirPath() + "/chat.ini";
    QSettings setting(filePath,QSettings::IniFormat);
    setting.setValue(key,QString::fromUtf8(Value.toUtf8().toBase64()));
    m_MyConfig.insert(key,Value);
    emit MyConfigChanged();
}

void ChatManage::exitCurrentUser()
{
    qDebug()<<"exit user now!";
    m_exit = true;
    p_Contrel->CloseTcp();
    //    pser->CloseTcp();
}

void ChatManage::copyString(QString str){
    qApp->clipboard()->setText( str );
}

QString ChatManage::copyFile(QString path)
{
    if(path.startsWith("file:"))
        path = path.replace("file:///","");
    QList<QUrl> copyfile;
    QUrl url=QUrl::fromLocalFile(path);
    if(url.isValid()){
        copyfile.push_back(url);
    }else{
        return "error:url is not Valid";
    }
    QMimeData *data=new QMimeData;
    data->setUrls(copyfile);

    qApp->clipboard()->setMimeData(data);
    return "success";
}

QString ChatManage::copyFileToPath(QString source,QString des,bool coverFileIfExist)
{
    if(source.startsWith("file:"))
        source = source.replace("file:///","");
    if(des.startsWith("file:"))
        des = des.replace("file:///","");

    QDir createFile;
    if(source==des){
        goto suc;
    }

    if(!QFile::exists(source)){
        return QStringLiteral("原文件已删除！");
    }

    if (createFile.exists(des)){
        if(coverFileIfExist){
            createFile.remove(des);
        }
    }

    if(!QFile::copy(source, des))
    {
        return "QFile::copy failed";
    }

    suc:
    return "success";
}
